/**
 * @file XListView.java
 * @package me.maxwin.view
 * @create Mar 18, 2012 6:28:41 PM
 * @author Maxwin
 * @description An ListView support (a) Pull down to refresh, (b) Pull up to load more.
 * Implement IXListViewListener, and see stopRefresh() / stopLoadMore().
 */
package com.lgs.common.widgets.xlist;


import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;

/**
 * @author liguoshuai
 * @category 列表框
 */

public class XListView extends ListView implements XListViewFooter.IXListViewFooterListener{

    private float mLastY = -1; // save event y
    private Scroller mScroller; // used for scroll back


    // -- header view
    private XListViewHeader mHeaderView;
    // header view content, use it to calculate the Header's height. And hide it
    // when disable pull refresh.
    private RelativeLayout mHeaderViewContent;
    private TextView mHeaderTimeView;
    private int mHeaderViewHeight; // header view's height
    private boolean mEnablePullRefresh = true;
    private boolean mPullRefreshing = false; // is refreashing.

    // -- footer view
    private XListViewFooter mFooterView;
    private boolean mEnablePullLoad = true;
    private boolean mPullLoading = false;
    private boolean mIsFooterReady = false;

    // total list items, used to detect is at the bottom of listview.
    private int mTotalItemCount;

    // for mScroller, scroll back from header or footer.
    private int mScrollBack;
    private final static int SCROLLBACK_HEADER = 0;
    private final static int SCROLLBACK_FOOTER = 1;

    private final static int SCROLL_DURATION = 400; // scroll back duration
    private final static int PULL_LOAD_MORE_DELTA = 50;
    // when pull up >= 50px at bottom, trigger load more.
    private final static float OFFSET_RADIO = 1.8f; // support iOS like pull feature.


    /**
     * @param context
     */
    public XListView(Context context) {
        super(context);
        initWithContext(context);
    }

    public XListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initWithContext(context);
    }

    public XListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initWithContext(context);
    }

    public void setFooterText(String hintInfo) {
        mFooterView.setHintInfo(hintInfo);
    }

    public void hideFooterText() {
        mFooterView.empty();
    }

    private void initWithContext(Context context) {
        mScroller = new Scroller(context, new DecelerateInterpolator());

        // user's scroll listener
        super.setOnScrollListener(new MyScrollListener());

        try {
            // init header view
            mHeaderView = new XListViewHeader(context);
            mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);
            mHeaderTimeView = (TextView) mHeaderView.findViewById(R.id.xlistview_header_time);
            addHeaderView(mHeaderView);
            // init header height
            mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    mHeaderViewHeight = mHeaderViewContent.getHeight();
                    getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }
            });

            // init footer view
            mFooterView = new XListViewFooter(context);
            mFooterView.setXListViewFooterListener(this);
        } catch (Exception e) {

        }
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        // make sure XListViewFooter is the last footer view, and only add once.
        if (mIsFooterReady == false) {
            mIsFooterReady = true;
            addFooterView(mFooterView);
        }
        super.setAdapter(adapter);
    }

    /**
     * enable or disable pull down refresh feature.
     *
     * @param enable
     */
    public void setPullRefreshEnable(boolean enable) {
        mEnablePullRefresh = enable;
        if (!mEnablePullRefresh) { // disable, hide the content
            mHeaderViewContent.setVisibility(View.INVISIBLE);
        } else {
            mHeaderViewContent.setVisibility(View.VISIBLE);
        }
    }

    /**
     * enable or disable pull up load more feature.
     *
     * @param enable
     */
    public void setPullLoadEnable(boolean enable) {
        mEnablePullLoad = enable;
        if (!mEnablePullLoad) {
            mFooterView.hide();
            mFooterView.setOnClickListener(null);
            // make sure "pull up" don't show a line in bottom when listview
            // with one page
            setFooterDividersEnabled(false);
        } else {
            mPullLoading = false;
            mFooterView.show();
            mFooterView.setState(XListViewFooter.STATE_NORMAL);
            // make sure "pull up" don't show a line in bottom when listview
            // with one page
            setFooterDividersEnabled(true);
            // both "pull up" and "click" will invoke load more.
            mFooterView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Load More
                    mPullLoading = true;
                    mFooterView.setState(XListViewFooter.STATE_LOADING);
                    if (mListViewListener != null) {
                        mListViewListener.onLoadMore();
                    }
                }
            });
        }
    }

    /**
     * stop refresh, reset header view.
     */
    public void stopRefresh() {
        if (mPullRefreshing == true) {
            mPullRefreshing = false;
            resetHeaderHeight();

            mDataUpdating = false;
        }
    }

    private boolean mDataUpdating = false;

    public boolean getDataUpdating() {
        return mDataUpdating;
    }

    public void setDataUpdating(boolean over) {
        mDataUpdating = over;
        if (!over) {
            stopRefresh();
            stopLoadMore();
            setRefreshTime("刚刚");
        }
    }

    /**
     * stop load more, reset footer view.
     */
    public void stopLoadMore() {
        if (mPullLoading == true) {
            mPullLoading = false;
            mFooterView.setState(XListViewFooter.STATE_NORMAL);

            mDataUpdating = false;
        }
    }

    /**
     * set last refresh time
     *
     * @param time
     */
    public void setRefreshTime(String time) {
        mHeaderTimeView.setText(time);
    }

    private void updateHeaderHeight(float delta) {
        mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight());
        if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态，更新箭头
            if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                mHeaderView.setState(XListViewHeader.STATE_READY);
            } else {
                mHeaderView.setState(XListViewHeader.STATE_NORMAL);
            }
        }
        setSelection(0); // scroll to top each time
    }

    /**
     * reset header view's height.
     */
    private void resetHeaderHeight() {
        int height = mHeaderView.getVisiableHeight();
        if (height == 0) // not visible.
            return;
        // refreshing and header isn't shown fully. do nothing.
        if (mPullRefreshing && height <= mHeaderViewHeight) {
            return;
        }
        int finalHeight = 0; // default: scroll back to dismiss header.
        // is refreshing, just scroll back to show all the header.
        if (mPullRefreshing && height > mHeaderViewHeight) {
            finalHeight = mHeaderViewHeight;
        }
        mScrollBack = SCROLLBACK_HEADER;
        mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);

        invalidate();// trigger computeScroll
    }

    private void updateFooterHeight(float delta) {
        int height = mFooterView.getBottomMargin() + (int) delta;
        if (mEnablePullLoad && !mPullLoading) {
            if (height > PULL_LOAD_MORE_DELTA) {
                // height enough to invoke load more. //松开显示更多
                mFooterView.setState(XListViewFooter.STATE_READY);
            } else {
                //查看更多
                mFooterView.setState(XListViewFooter.STATE_NORMAL);
            }
        }
        mFooterView.setBottomMargin(height);

        // setSelection(mTotalItemCount - 1); // scroll to bottom
    }

    private void resetFooterHeight() {
        int bottomMargin = mFooterView.getBottomMargin();
        if (bottomMargin > 0) {
            mScrollBack = SCROLLBACK_FOOTER;
            mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION);
            invalidate();
        }
    }

    @Override
    public void onClickLoadMore() {
        callLoadMore();
    }

    class MyScrollListener implements OnScrollListener {
//列表加载更多有3种方式，
// 1、手指拖动松开判断是否加载更多
// 2、点击文字（加载更多），一般一二方式同时使用，用来listview比较多
        //3、只要接近尾部，或者显示了底部栏就加载更多，recycleview中使用。
        //不管是哪种方式，一定要预先设计好，而不能做到哪里算哪里，到最后可能时间无法控制，也不清楚自己到底想要什么。

        //int currentIndex = 0;
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

//            switch (scrollState) {
//                // 滚动之前,手还在屏幕上  记录滚动前的下标
//                case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//                    //view.getLastVisiblePosition()得到当前屏幕可见的第一个item在整个listview中的下标
//                    currentIndex = view.getLastVisiblePosition();
//                    break;
//
//                case OnScrollListener.SCROLL_STATE_IDLE: //滚动停止
//                case OnScrollListener.SCROLL_STATE_FLING://正在滚动
//                    //记录当前item的位置
//                    int scrolled = view.getLastVisiblePosition();
//
//                    if (scrolled > currentIndex) { //滚动后下标大于滚动前 向下滚动了
//                        callLoadMore();
//                    }
//                     else { //向上滚动了
//                        callRefresh();
//                    }
//                    break;
//            }


        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            mTotalItemCount = totalItemCount;
        }
    }

    private void callRefresh() {
        if (getFirstVisiblePosition() == 0) {
            // invoke refresh //mHeaderViewHeight头部原始高度
            if (mEnablePullRefresh && !mPullRefreshing && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
                mPullRefreshing = true;
                mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
                if (mListViewListener != null) {
                    mListViewListener.onRefresh();
                }
            } else {
                stopRefresh();
            }
            resetHeaderHeight();
        }

    }

    private void callLoadMore() {
        if (getLastVisiblePosition() == mTotalItemCount - 1) {
            // invoke load more.
            if (mEnablePullLoad && !mPullLoading) {
                //// Load More
                mPullLoading = true;
                mFooterView.setState(XListViewFooter.STATE_LOADING);
                if (mListViewListener != null) {
                    mListViewListener.onLoadMore();
                }
            } else {
                stopLoadMore();
            }
            resetFooterHeight();
        }
    }

    private int mMoveDirect = 0;//1向下拖动，-1向上拖动

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mLastY == -1) {
            mLastY = ev.getRawY();
        }
        int actionId = ev.getAction();

        switch (actionId) {
            case MotionEvent.ACTION_DOWN:
                performClick();

                mLastY = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                final float deltaY = ev.getRawY() - mLastY;//移动距离
                mLastY = ev.getRawY();
                if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
                    // the first item is showing, header has shown or pull down.
                    updateHeaderHeight(deltaY / OFFSET_RADIO);
                    mMoveDirect = 1;
                } else if (getLastVisiblePosition() == mTotalItemCount - 1//显示最后一行
                        && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {//显示底部栏
                    // last item, already pulled up or want to pull up.
                    // updateFooterHeight(-deltaY / OFFSET_RADIO);//会导致下边界滑动

                    updateFooterHeight(mLastY / OFFSET_RADIO);//设置固定位置

                    mMoveDirect = -1;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP: {
                mLastY = -1; // reset
                if (mMoveDirect == 1) {
                    callRefresh();
                } else if (mMoveDirect == -1) {
                    if (mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
                        callLoadMore();
                    }
                }
                mMoveDirect = 0;
            }
            break;
            default:

                break;
        }
        return super.onTouchEvent(ev);
    }

    @Override
    public boolean performClick() {
        super.performClick();
        return true;
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) { //重新调整位置，避免出现混乱
            if (mScrollBack == SCROLLBACK_HEADER) {
                mHeaderView.setVisiableHeight(mScroller.getCurrY());
            } else {
                mFooterView.setBottomMargin(mScroller.getCurrY());
            }
            postInvalidate();
        }
        super.computeScroll();
    }

    // the interface to trigger refresh and load more.
    private IXListViewListener mListViewListener;
    public void setXListViewListener(IXListViewListener l) {
        mListViewListener = l;
    }

    /**
     * implements this interface to get refresh/load more event.
     */
    public interface IXListViewListener {
        public void onRefresh();

        public void onLoadMore();
    }

}
